package com.dbapp.garden.controller.sys;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.dbapp.garden.config.security.PermissionUser;
import com.dbapp.garden.entity.SysResource;
import com.dbapp.garden.entity.SysUser;
import com.dbapp.garden.service.SysResourceService;
import com.dbapp.garden.service.SysUserService;
import com.dbapp.garden.vo.Menu;

@CrossOrigin(origins = "*", maxAge = 3600)
@Controller
public class LoginController {

	@Autowired
	private SysUserService userService;
	
	@Autowired
	private SysResourceService sysResourceService;

	@Autowired
	private AuthenticationManager myAuthenticationManager;

//	@Autowired
//	private BCryptPasswordEncoder bCryptPasswordEncoder;

	@RequestMapping(value = { "/", "/login" }, method = RequestMethod.GET)
	public ModelAndView login() {
		
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("login/login");
		// 1.如果想在程序中获得当前登陆用户对应的对象
		// UserDetails userDetails = (UserDetails)
		// SecurityContextHolder.getContext().getAuthentication().getPrincipal();

		// 2.如果想获得当前登陆用户所拥有的所有权限。
		// Collection<GrantedAuthority> authorities =
		// (Collection<GrantedAuthority>) userDetails.getAuthorities();;
		return modelAndView;
	}

	@ResponseBody
	@RequestMapping(value = { "/login2", "/login3" }, method = RequestMethod.GET)
	// @PreAuthorize("hasAuthority('ROLE_TELLER')")
	public UserDetails login2(HttpServletRequest request, HttpSession session) {

		// 1.如果想在程序中获得当前登陆用户对应的对象
		UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

		// 2.如果想获得当前登陆用户所拥有的所有权限。
		// Collection<GrantedAuthority> authorities =
		// (Collection<GrantedAuthority>) userDetails.getAuthorities();
		System.out.println("sessionid是：" + session.getId());
		return userDetails;
	}

	@RequestMapping(value = "/registration", method = RequestMethod.GET)
	public ModelAndView registration(ModelAndView modelAndView) {

		SysUser user = new SysUser();
		modelAndView.addObject("user", user);
		modelAndView.setViewName("registration");
		return modelAndView;
	}

	@RequestMapping(value = "/registration", method = RequestMethod.POST)
	public ModelAndView createNewUser(@Valid SysUser user, BindingResult bindingResult) {
		ModelAndView modelAndView = new ModelAndView();
		SysUser userExists = userService.findUserByUserName(user.getUserName());
		if (userExists != null) {
			bindingResult.rejectValue("email", "error.user",
					"There is already a user registered with the email provided");
		}
		if (bindingResult.hasErrors()) {
			modelAndView.setViewName("registration");
		} else {
			userService.saveUser(user);
			modelAndView.addObject("successMessage", "User has been registered successfully");
			modelAndView.addObject("user", new SysUser());
			modelAndView.setViewName("registration");

		}
		return modelAndView;
	}

	@RequestMapping(value = "/index", method = RequestMethod.GET)
	//@PreAuthorize("hasAuthority('PERMISSION_ADMIN')")
	public ModelAndView home() {
		ModelAndView modelAndView = new ModelAndView();
		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
		SysUser user = userService.findUserByUserName(auth.getName());
		modelAndView.addObject("userName", "Welcome " + user.getUserName() + " ");
		modelAndView.addObject("adminMessage", "Content Available Only for Users with Admin Role");
		modelAndView.setViewName("index/index");
		return modelAndView;
	}

	@PostMapping("/loginCheck")
	public String loginCheck(String username, String password, HttpServletRequest request,HttpSession session) {
		try {
			Authentication authentication = myAuthenticationManager
					.authenticate(new UsernamePasswordAuthenticationToken(username, password));
			SecurityContext securityContext = SecurityContextHolder.getContext();
			securityContext.setAuthentication(authentication);
			
			session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
			// 当验证都通过后，把用户信息放在session里
			request.getSession().setAttribute("userSession", username);
			// 记录登录信息
			
			PermissionUser userDetails = (PermissionUser)
			SecurityContextHolder.getContext().getAuthentication().getPrincipal();
			
			Collection<GrantedAuthority> authorities =(Collection<GrantedAuthority>) userDetails.getAuthorities();
			//3.获取菜单放入到Session中，这里模拟从数据库获取
			Integer userid=userDetails.getUid();
			List<SysResource> resourceList=sysResourceService.getUserResources(userid);
			List<Menu> menus=parseMenu(resourceList);
			
//			
			session.setAttribute("menus", menus);
			session.setAttribute("authorities", authorities);
			session.setAttribute("user", userDetails);
		} catch (AuthenticationException ae) {
			request.setAttribute("error", "登录异常，请联系管理员！");
			System.out.println("登录异常，请联系管理员！");
			return "redirect:/login?error=true";
		}
		return "redirect:/index";
	}
	private List<Menu> parseMenu(List<SysResource> resourceList) {
		if(resourceList==null){
			return null;
		}
		List<Menu> menus=new LinkedList<>();
		for (SysResource sysResource : resourceList) {
			Integer menuType = sysResource.getResType();
			//获取一级菜单
			if (1 == menuType&&sysResource.getResParentId()==0) {// 1 代表是菜单；2代表Action
				Menu menu = changeResource2Menu(resourceList, sysResource);
				menus.add(menu);		
		}
	}
		//排序
		Collections.sort(menus, new Comparator<Menu>() {
            @Override
            public int compare(Menu m1, Menu m2) {
                return m1.getSort().compareTo(m2.getSort());
            }
        });
		return menus;
}
	

	/**
	 * 将用户的资源转成菜单
	 * @param resourceList
	 * @param sysResource
	 * @return
	 */
	private Menu changeResource2Menu(List<SysResource> resourceList, SysResource sysResource) {
		Menu menu=new Menu();
		menu.setId(sysResource.getId());
		menu.setName(sysResource.getResName());
		menu.setParentId(sysResource.getResParentId());
		menu.setUrl(sysResource.getResUrl());
		menu.setSort(sysResource.getResSort());
		menu.setIcon(sysResource.getResIcon());
		List<Menu> childMenus = getChildMenus(sysResource.getId() ,resourceList);
		List<String> childUrls = getChildUrls(childMenus);
		if(!childMenus.isEmpty()){
			menu.setChildMenu(childMenus);
		}
		if(!childUrls.isEmpty()){
			menu.setChildUrls(childUrls);			
		}
		return menu;
	}

	/**
	 * 将子菜单的URL封装成集合
	 * @param childMenus
	 * @return
	 */
	private List<String> getChildUrls(List<Menu> childMenus) {
		return childMenus.stream().map(m->{
			return m.getUrl();
		}).collect(Collectors.toList());
	}

	/**
	 * 获取一级菜单的子菜单
	 * @param id
	 * @param resourceList
	 * @return
	 */
	private List<Menu> getChildMenus(Integer pid, List<SysResource> resourceList) {
		if (resourceList == null) {
			return null;
		}
		List<Menu> childMenu = new LinkedList<>();
		for (SysResource r : resourceList) {
			Integer menuType = r.getResType();
			if(1==menuType&&pid==r.getResParentId()){
				Menu m = changeResource2Menu(resourceList, r);
				childMenu.add(m);
			}
		}
		return childMenu;
	}
}
